package common.config.knife4j;

import common.model.enums.EnvironmentEnum;
import common.util.Constants;
import common.util.PublicUrlAntPatternUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;

/**
 * knife4j配置类
 * <br/>
 * 网址：<a href="http://localhost:8888/doc.html">http://localhost:8888/doc.html</a>
 *
 * @author 米泽鹏
 * @since 2022-11-23 下午 6:20
 */
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {

	@Value("${spring.application.name}")
	private String appName;

	@Bean
	public Docket docket(@NotNull Environment environment) {
		return new Docket(DocumentationType.SWAGGER_2)
				.apiInfo(buildApiInfo(appName + "服务接口文档", "接口文档"))
//				.groupName(Docket.DEFAULT_GROUP_NAME)
				.securitySchemes(Collections.singletonList(securitySchemes()))
				.securityContexts(Collections.singletonList(securityContexts()))
				.select()
				// 位于controller包下
				.apis(RequestHandlerSelectors.basePackage(appName + ".controller"))
				// 不以 /test/ 开头
//				.paths(PathSelectors.regex("(?:(?!^/test/).)*"))
				// 只显示加了@Api注解的controller
//				.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
				// 只显示加了@ApiOperation注解的接口方法
//				.apis(RequestHandlerSelectors.withClassAnnotation(ApiOperation.class))
				.build()
				.ignoredParameterTypes(HttpServletResponse.class, HttpServletRequest.class)
				// 设置显示该docket的环境（满足一个即可）
				.enable(environment.acceptsProfiles(Profiles.of(EnvironmentEnum.DEV.getName(), EnvironmentEnum.TEST.getName())));
	}

	private ApiInfo buildApiInfo(String title, String description) {
		return new ApiInfoBuilder()
				.title(title)
				.description(description)
				.contact(new Contact("米泽鹏", "https://blog.csdn.net/mizepeng", "mizepeng@hotmail.com"))
				.version("1.0.0")
				.build();
	}

	/**
	 * 通过 securitySchemes 来配置全局参数，这里的配置是一个名为 AuthorizationBearer 的请求头
	 */
	@Contract(" -> new")
	private @NotNull SecurityScheme securitySchemes() {
		return new ApiKey(Constants.AUTHORIZATION_HEADER, Constants.AUTHORIZATION_HEADER, "header");
	}

	/**
	 * 配置有哪些请求需要携带token
	 */
	private SecurityContext securityContexts() {
		return SecurityContext.builder()
				.securityReferences(defaultAuth())
				.forPaths(url -> !PublicUrlAntPatternUtil.isPublicUrl(url))
				.build();
	}

	private @NotNull @Unmodifiable List<SecurityReference> defaultAuth() {
		AuthorizationScope authorizationScope = new AuthorizationScope("scope", "description");
		AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
		authorizationScopes[0] = authorizationScope;
		return Collections.singletonList(new SecurityReference(Constants.AUTHORIZATION_HEADER, authorizationScopes));
	}

}
